package com.zjny.util;

import cn.hutool.core.util.ReUtil;
import com.zjny.constant.CommonConstant;
import com.zjny.constant.ToolRegexConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * packageName com.zjny.util
 *
 * @author cui haida
 * @version JDK 8
 * 2025/6/18
 */
@Slf4j
public class ReUtils extends ReUtil {
    // 分组标识
    public final static Pattern GROUP_VAR = Pattern.compile("\\$(\\d+)");

    private static final String[] EN_SYMBOL = {
            "'", "\"", "\\[", "]", "\\{", "}", ",", ":", "<", ">", "\\?", "\\(", "\\)", "!"
    };

    private static final String[] CN_SYMBOL = {
            "‘", "”", "【", "】", "｛", "｝", "，", "：", "《", "》", "？", "（", "）", "！"
    };

    // 正则中需要被转义的关键字
    public final static Set<Character> RE_KEYS = new HashSet<>(
            Arrays.asList('$', '(', ')', '*', '+', '.', '[', ']', '?', '\\', '^', '{', '}', '|'));

    /**
     * 检查提供的字符串是否为有效的正则表达式。
     *
     * @param regex 待检查的正则表达式字符串。
     * @return 返回{@code true}如果正则表达式有效，否则返回{@code false}。
     */
    public static boolean checkRegex(String regex) {
        try {
            // 尝试编译正则表达式，如果成功则表示正则表达式有效
            Pattern.compile(regex);
            return true;
        } catch (Exception e) {
            // 如果编译失败（即抛出异常），则表示正则表达式无效
            return false;
        }
    }

    /**
     * 正则替换指定值<br>
     * 通过正则查找到字符串，然后把匹配到的字符串加入到replacementTemplate中，$1表示分组1的字符串
     * 例如：原字符串是：中文1234，我想把1234换成(1234)，则可以：
     * ReUtil.replaceAll("中文1234", "(\\d+)", "($1)"))
     *
     * @param content             文本
     * @param regex               正则
     * @param replacementTemplate 替换的文本模板，可以使用$1类似的变量提取正则匹配出的内容
     * @return 处理后的文本
     */
    public static String replaceAll(CharSequence content, String regex, String replacementTemplate) {
        final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
        return replaceAll(content, pattern, replacementTemplate);
    }

    /**
     * 使用正则表达式替换指定的内容。
     * <p>此方法通过正则表达式查找到字符串中的匹配项，然后将匹配项根据replacementTemplate的规则进行替换。
     * replacementTemplate中可以使用$1这样的变量来引用匹配到的内容。</p>
     *
     * @param content             需要处理的文本。
     * @param pattern             用于查找匹配项的正则表达式模式。
     * @param replacementTemplate 替换匹配项的模板字符串。模板中可通过$1的形式引用匹配到的内容。
     * @return 替换处理后的文本。如果输入文本为空或正则没有匹配到任何内容，则返回原文本。
     * @since 3.0.4
     */
    public static String replaceAll(CharSequence content, Pattern pattern, String replacementTemplate) {
        // 检查输入文本是否为空
        if (StringUtils.isEmpty(content)) {
            return StringUtils.EMPTY;
        }

        // 创建匹配器并查找第一个匹配项
        final Matcher matcher = pattern.matcher(content);
        boolean result = matcher.find();
        if (result) {
            // 在替换模板中找到所有引用的匹配组号
            final Set<String> varNums = findAll(GROUP_VAR, replacementTemplate, 1, new HashSet<>());
            // 用于存放替换结果的字符串缓冲区
            final StringBuffer sb = new StringBuffer();
            do {
                // 准备进行替换的字符串
                String replacement = replacementTemplate;
                // 替换模板中的引用变量为实际匹配到的字符串
                for (String var : varNums) {
                    int group = Integer.parseInt(var);
                    replacement = replacement.replace("$" + var, matcher.group(group));
                }
                // 将替换结果安全地（可能包含特殊正则字符）添加到结果缓冲区
                matcher.appendReplacement(sb, escape(replacement));
                // 继续查找下一个匹配项
                result = matcher.find();
            }
            while (result); // 如果还有匹配项，则继续替换
            // 将剩余的文本添加到结果中
            matcher.appendTail(sb);
            return sb.toString();
        }
        // 如果没有匹配项，则直接返回原文本
        return ConvertUtils.toStr(content);
    }

    /**
     * 取得内容中匹配的所有结果
     *
     * @param <T>        集合类型
     * @param pattern    编译后的正则模式
     * @param content    被查找的内容
     * @param group      正则的分组
     * @param collection 返回的集合类型
     * @return 结果集
     */
    public static <T extends Collection<String>> T findAll(Pattern pattern, CharSequence content, int group, T collection) {
        if (null == pattern || null == content) {
            return null;
        }

        if (null == collection) {
            throw new NullPointerException("Null collection param provided!");
        }

        final Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            collection.add(matcher.group(group));
        }
        return collection;
    }

    /**
     * 转义字符，将正则表达式中的关键字字符进行转义。
     * 如果字符是正则表达式中的特殊关键字，会在该字符前添加反斜杠进行转义。
     *
     * @param c 需要转义的字符。
     * @return 转义后的字符串。如果字符是特殊关键字，则返回转义后的字符串；否则返回原字符。
     */
    public static String escape(char c) {
        final StringBuilder builder = new StringBuilder();
        // 检查字符是否是正则表达式的关键字，若是，则进行转义
        if (RE_KEYS.contains(c)) {
            builder.append('\\');
        }
        // 将字符添加到StringBuilder中
        builder.append(c);
        return builder.toString();
    }


    /**
     * 转义字符串，将正则表达式中的关键字字符进行转义。
     * 该方法主要处理传入文本中的每个字符，如果字符是正则表达式中的特殊关键字，则在该字符前添加反斜杠进行转义。
     *
     * @param content 需要进行转义处理的文本。
     * @return 转义后的文本字符串。如果输入为空或仅包含空白字符，则返回空字符串。
     */
    public static String escape(CharSequence content) {
        // 检查输入文本是否为空或仅包含空白字符
        if (StringUtils.isBlank(content)) {
            return StringUtils.EMPTY;
        }

        // 使用StringBuilder来构建转义后的字符串
        final StringBuilder builder = new StringBuilder();
        int len = content.length(); // 获取文本长度
        char current;
        // 遍历文本中的每个字符
        for (int i = 0; i < len; i++) {
            current = content.charAt(i); // 获取当前字符
            // 检查当前字符是否为正则表达式中的特殊关键字
            if (RE_KEYS.contains(current)) {
                builder.append('\\'); // 如果是，则在字符前添加反斜杠进行转义
            }
            builder.append(current); // 将当前字符添加到构建器中
        }
        return builder.toString(); // 返回转义后的字符串
    }

    /**
     * 获取文本中的所有的数字
     *
     * @param content 内容文本
     * @return 所有的数字集合
     */
    public static List<Integer> findAllDigit(String content) {
        if (StringUtils.isEmpty(content)) {
            return new ArrayList<>();
        }

        Matcher matcher = ToolRegexConstant.NUMBER_PATTERN.matcher(content);
        List<Integer> numbers = new ArrayList<>();
        while (matcher.find()) {
            numbers.add(NumberUtils.toInt(matcher.group()));
        }
        return numbers;
    }

    /**
     * 获取文本中的第一个数字串
     *
     * @param content 内容
     * @return 结果
     */
    public static Integer getNumber(String content) {
        if (StringUtils.isEmpty(content)) {
            return null;
        }
        Matcher matcher = ToolRegexConstant.NUMBER_PATTERN.matcher(content);
        // 抓取第一个数字串进行转换
        if (matcher.find()) {
            return NumberUtils.toInt(matcher.group());
        }
        return null;
    }

    /**
     * 根据给定的正则表达式和目标字符串尝试匹配。
     *
     * @param reg    正则表达式字符串，用于匹配目标字符串。
     * @param target 需要被匹配的目标字符串。
     * @return 如果找到匹配，则返回一个Matcher对象；如果没有找到匹配或匹配失败，则返回null。
     */
    public static Matcher match(String reg, String target) {
        // 创建一个Matcher对象用于匹配
        Matcher matcher = getMatcher(reg, target);
        if (matcher == null) {
            // 如果无法创建Matcher对象（可能是正则表达式错误），则直接返回null
            return null;
        }
        if (matcher.find()) {
            // 使用matcher查找目标字符串中的第一个匹配项
            return matcher;
        }
        // 如果没有找到匹配项，则返回null
        return null;
    }


    /**
     * 根据提供的正则表达式和目标字符串创建并返回一个Matcher对象。
     * 如果正则表达式或目标字符串为空，或者正则表达式编译失败，则返回null。
     *
     * @param reg       正则表达式字符串，用于创建Pattern对象。
     * @param targetStr 需要被匹配的目标字符串。
     * @return 返回一个Matcher对象，如果无法创建则返回null。
     */
    private static Matcher getMatcher(String reg, String targetStr) {
        // 检查输入字符串是否为空
        if (StringUtils.isEmpty(reg) || StringUtils.isEmpty(targetStr)) {
            return null;
        }
        try {
            // 编译正则表达式并创建Matcher对象
            Pattern pattern = Pattern.compile(reg);
            return pattern.matcher(targetStr);
        } catch (PatternSyntaxException e) {
            // 记录正则表达式编译错误
            log.error("正则错误：{}", e.toString());
        }
        return null;
    }


    /**
     * 替换group(1)中的值
     *
     * @param reg         规则
     * @param target      原文本
     * @param replacement 替换后的文本
     * @return 替换后的结果
     */
    public static String replaceGroupOne(String reg, String target, String replacement) {
        if (StringUtils.isEmpty(reg) || StringUtils.isEmpty(target) || StringUtils.isEmpty(replacement)) {
            return target;
        }
        return replaceGroupOne(Pattern.compile(reg), target, replacement);
    }

    /**
     * 替换group(1)中的值。该方法通过给定的正则表达式模式和替换文本，对原始文本进行处理，
     * 将匹配模式中第一个分组（group 1）的内容替换为指定的替换文本。
     *
     * @param pattern     正则编译对象，用于匹配文本。不可为null。
     * @param target      原文本，将对此文本进行模式匹配和替换。不可为null。
     * @param replacement 替换后的文本，将用此文本替换匹配到的第一个分组的内容。不可为null。
     * @return 替换结果，返回替换操作后的新字符串。
     */
    private static String replaceGroupOne(Pattern pattern, String target, String replacement) {
        // 检查输入参数，如果有任何参数为null，则直接返回原文本。
        if (pattern == null || target == null || replacement == null) {
            return target;
        }
        Matcher matcher = pattern.matcher(target);
        StringBuilder builder = new StringBuilder();
        int i = 0; // 记录上一次匹配结束的位置，用于下一次匹配的开始位置。

        // 遍历文本中所有匹配的项。
        while (matcher.find()) {
            try {
                // 将从上一次匹配结束位置到当前匹配项开始位置的文本添加到结果中。
                builder.append(target, i, matcher.start(1));
                // 添加替换后的文本。
                builder.append(replacement);
                // 更新i为当前匹配项的结束位置，为下一次匹配做准备。
                i = matcher.end(1);
            } catch (Exception e) {
                // 如果匹配的分组不存在（即group 1无效），则将从上一次匹配结束位置到当前匹配项开始位置的文本，
                // 添加到结果中，并添加替换文本。然后更新i为当前匹配项的结束位置。
                builder.append(target, i, matcher.start());
                builder.append(replacement);
                i = matcher.end();
                // 记录日志，指出group 1不存在的错误。
                log.error("要素指代规则[{}]不存在group1", pattern.pattern());
            }
        }
        // 将从最后一次匹配结束位置到文本末尾的文本添加到结果中。
        builder.append(target.substring(i));
        return builder.toString();
    }

    /**
     * 是否可以匹配
     *
     * @param reg    正则表达式
     * @param target 目标字符串
     * @return 正则匹配对象
     */
    public static boolean matchStatus(String reg, String target) {
        if (StringUtils.isEmpty(reg)) {
            return false;
        }
        Matcher matcher = match(reg, target);
        if (matcher == null) {
            return false;
        }
        return matcher.matches();
    }

    /**
     * 替换为空
     *
     * @param reg    正则
     * @param target 目标字符串
     * @return 将满足正则条件的替换为空
     */
    public static String replaceToEmpty(String reg, String target) {
        return Pattern.compile(reg).matcher(target).replaceAll(CommonConstant.EMPTY);
    }

    /**
     * 通过字符切割
     *
     * @param str 字符串
     * @param reg 切割字符
     * @return 切割结果
     */
    public static String[] split(String str, String reg) {
        if (StringUtils.isEmpty(str)) {
            return new String[]{};
        }
        return str.split(reg);
    }

    /**
     * 空格去除
     *
     * @param str 给定的文本字符串
     * @return 去除空格后的结果
     */
    public static String removeSpace(String str) {
        return replaceToEmpty("\\s*", str);
    }

    /**
     * 将英文符号转换成中文符号
     * @param content  内容
     * @return 转换结果
     */
    public static String EnToCnSymbol(String content) {
        for (int i = 0; i < EN_SYMBOL.length; i++) {
            content = Pattern.compile(EN_SYMBOL[i]).matcher(content).replaceAll(CN_SYMBOL[i]);
        }
        return content;
    }

    /**
     * 将中文符号转换成英文符号
     * @param content  内容
     * @return 转换结果
     */
    public static String CnToEnSymbol(String content) {
        for (int i = 0; i < EN_SYMBOL.length; i++) {
            content = Pattern.compile(CN_SYMBOL[i]).matcher(content).replaceAll(EN_SYMBOL[i]);
        }
        return content;
    }

    /**
     * 判断是否为纯数字
     *
     * @param content 内容
     * @return 结果
     */
    public static boolean isNumber(String content) {
        Matcher numMatcher = match("[1-9]", content);
        return numMatcher != null;
    }

    /**
     * 判断目标字符串是否包含特定字符，如果包含，则返回
     *
     * @param arr 判断数组
     * @param str 目标字符串
     * @return 如果包含数组中的内容将返回找到的第一个，如果一个都找不到返回null
     */
    public static String searchContainStr(String[] arr, String str) {
        Optional<String> optional = Arrays.stream(arr).filter(str::contains).findFirst();
        return optional.orElse(null);
    }


    /**
     * 判断是否为中文首字母
     * @param str 给定的中文字符串
     * @return 判断结果 if true -> 是中文首字母, if false -> 不是中文首字母
     */
    public static boolean isChineseFirst(String str) {
        return str.matches(ToolRegexConstant.CHINESE_REG);
    }
}